/* 
 * Copyright (c) 2017 Omar Vega Ramos.
 * 
 * This program is free software: you can redistribute it and/or modify  
 * it under the terms of the GNU General Public License as published by  
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version..
 *
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License 
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <expat.h>

#define n 35000

char object_id[255];
bool is_place = false;
char name[1024];
double lat;
double lon;
size_t total = 0;

struct coord
{
  char id[255];
  double lat;
  double lon;
};

struct place
{
  char name[1024];
  size_t subtotal;
  struct coord coords[150];
};

struct place places[n];

bool
are_nearby(struct coord x, struct coord y) {
  if ((fabs(x.lat - y.lat) < 0.005) && (fabs(x.lon - y.lon) < 0.005)) {
    return true;
  } else {
    return false;
  }
}

int
find_index_place(const char *str) {
  size_t i = 0;
  bool found = false;

  while((i < total) && (found == false)) {
    if (strcmp(places[i].name, str) == 0) {
      found = true;
    }

    i++;
  }

  if (found == true) {
    return (i - 1);
  } else {
    return -1;
  }
}

void
start_element(void *data, const char *element, const char **attrs)
{
  if (strcmp(element, "node") == 0) {
    strcpy(object_id, attrs[1]);
    lat = atof(attrs[3]);
    lon = atof(attrs[5]);
  }

  if ((strcmp(element, "tag") == 0) && (strcmp(object_id, "") != 0)) {
    if (strcmp(attrs[1], "place") == 0) {
      // Skip island
      if (strcmp(attrs[3], "island") != 0) {
	is_place = true;
      }
    }

    if (strcmp(attrs[1], "name") == 0) {
      strcpy(name, attrs[3]);
    }
  }
}

void
end_element(void *data, const char *element)
{
  if (strcmp(element, "node") == 0) {
    if ((is_place == true) && (strcmp(name, "") != 0)) {
      int j = find_index_place(name);

      if (j != -1) {
	// Add coordinate to place with same name
	int subtotal = places[j].subtotal;
	strcpy(places[j].coords[subtotal].id, object_id);
	places[j].coords[subtotal].lat = lat;
	places[j].coords[subtotal].lon = lon;
	places[j].subtotal = subtotal + 1;
      } else {
	// Add new place
	strcpy(places[total].name, name);
	places[total].subtotal = 1;
	strcpy(places[total].coords[0].id, object_id);
	places[total].coords[0].lat = lat;
	places[total].coords[0].lon = lon;
	total = total + 1;
      }
    }

    strcpy(object_id, "");
    is_place = false;
    strcpy(name, "");
  }
}

int
main(int argc, char *argv[])
{
  if (argc != 2) {
    fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
    exit(-1);
  }

  FILE *fp = fopen(argv[1], "r");

  if (fp == NULL) {
    fprintf(stderr, "Could not read %s\n", argv[1]);
    exit(-1);
  }

  int len;
  char buf[1024];
  int done;

  XML_Parser parser = XML_ParserCreate(NULL);
  XML_SetElementHandler(parser, start_element, end_element);

  do {
    len = (int)fread(buf, 1, sizeof(buf), fp);
    done = len < sizeof(buf);

    if (XML_Parse(parser, buf, len, done) == XML_STATUS_ERROR) {
      int code = XML_GetCurrentLineNumber(parser);
      const char *msg = XML_ErrorString(XML_GetErrorCode(parser));
      fprintf(stderr, "%s at line %d\n", msg, code);
      exit(-1);
    }
  } while (!done);

  XML_ParserFree(parser);

  for (int i = 0; i < total; i++) {
    int subtotal = places[i].subtotal;

    if (subtotal > 1) {
      for (int j = 0; j < (subtotal - 1); j++) {
	for (int k = (j + 1); k < subtotal; k++) {
	  if (are_nearby(places[i].coords[j], places[i].coords[k])) {
	    // Print nearby places
	    printf("%s, n%s, n%s\n", places[i].name, places[i].coords[j].id, places[i].coords[k].id);
	  }
	}
      }
    }
  }

  return 0;
}
